<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />

    <title>Web Audio API examples: decodeAudioData() with XHR</title>
  </head>

  <body>
    <h1>Web Audio API examples: decodeAudioData() with XHR</h1>

    <button id="play" disabled>Play</button>
    <button id="stop">Stop</button>

    <h2>Set playback rate</h2>
    <input
      id="playback-rate-control"
      type="range"
      min="0.25"
      max="3"
      step="0.05"
      value="1"
      disabled />
    <span id="playback-rate-value">1.0</span>

    <h2>Set loop start and loop end</h2>
    <input
      id="loopstart-control"
      type="range"
      min="0"
      max="20"
      step="1"
      value="0"
      disabled />
    <span id="loopstart-value">0</span>

    <input
      id="loopend-control"
      type="range"
      min="0"
      max="20"
      step="1"
      value="0"
      disabled />
    <span id="loopend-value">0</span>
  </body>
  <script>
    window.addEventListener("DOMContentLoaded", loadPage, false);

    // define variables
    const audioCtx = new AudioContext();
    let buffer;
    let source;

    const play = document.getElementById("play");
    const stop = document.getElementById("stop");

    const playbackControl = document.getElementById("playback-rate-control");
    const playbackValue = document.getElementById("playback-rate-value");

    const loopstartControl = document.getElementById("loopstart-control");
    const loopstartValue = document.getElementById("loopstart-value");

    const loopendControl = document.getElementById("loopend-control");
    const loopendValue = document.getElementById("loopend-value");

    function loadPage() {
      getAudio("viper");
    }

    // getAudio() has no return value
    // decoded AudioBuffer is buf argument to callback function
    // it uses XHR to load an audio file
    // it uses decodeAudioData to decode it into an AudioBuffer
    // play.onclick() create single-use AudioBufferSourceNode
    function getAudio(name) {
      request = new XMLHttpRequest();
      request.open("GET", `${name}.mp3`, true);
      request.responseType = "arraybuffer";
      request.onload = () => {
        let audioData = request.response;
        audioCtx.decodeAudioData(
          audioData,
          (buf) => {
            // executes when buffer has been decoded
            buffer = buf;
            const max = Math.floor(buf.duration);
            loopstartControl.max = max;
            loopendControl.max = max;
            play.disabled = false; // buffer loaded, enable play button
          },
          (err) => {
            console.error(
              `Unable to get the audio file: ${name} Error: ${err.message}`
            );
          }
        );
      };
      request.send();
    }

    play.onclick = () => {
      source = audioCtx.createBufferSource();
      source.buffer = buffer;
      source.playbackRate.value = playbackControl.value;
      source.connect(audioCtx.destination);
      source.loop = true;
      source.start();
      play.disabled = true;
      playbackControl.disabled = false;
      loopstartControl.disabled = false;
      loopendControl.disabled = false;
    };

    stop.onclick = () => {
      source.stop();
      play.disabled = false;
      playbackControl.disabled = true;
      loopstartControl.disabled = true;
      loopendControl.disabled = true;
    };

    playbackControl.oninput = () => {
      source.playbackRate.value = playbackControl.value;
      playbackValue.textContent = playbackControl.value;
    };

    loopstartControl.oninput = () => {
      source.loopStart = loopstartControl.value;
      loopstartValue.textContent = loopstartControl.value;
    };

    loopendControl.oninput = () => {
      source.loopEnd = loopendControl.value;
      loopendValue.textContent = loopendControl.value;
    };
  </script>
</html>
